/*
 *  Copyright 2009 Advanced Industrial Science and Technology (AIST).
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file
 *  \brief
 */
#pragma once

// thrust, includes
#include <thrust/device_vector.h>
#include <thrust/device_ptr.h>
#include <thrust/pending/integer_range.hpp>

// cuda, includes
#include <cuda_runtime.h>

#include <iostream>
//#include <typeinfo>

#include <thrust/mpl/bool.hpp>

namespace thrust {

  // stored_vertex
  template <typename SizeType, typename VertexProperty>
  struct stored_vertex {
    
    typedef SizeType size_type;
    
  public:
    thrust::integer_range<size_type,thrust::device_space_tag> m_out_edges;
    VertexProperty m_property;


  public:
    stored_vertex() {}
    
  public:
    __host__ __device__
    inline size_type
    getOutDegree() const 
    {
      return m_out_edges.size();
    }

    __host__ __device__
    inline thrust::pair<size_type,size_type>
    out_edges() const
    {
      return thrust::make_pair( *(m_out_edges.begin()), *(m_out_edges.end()));
    }

    __host__ __device__
    inline void
    set_out_edges( size_type start, size_type finish)
    {
      m_out_edges.set( start, finish);
    }

    __host__ __device__
    inline void
    inc_out_edges()
    {
      size_type start, finish;
      thrust::tie( start, finish) =m_out_edges.get();
      m_out_edges.set( start, ++finish);
    }

    __host__ __device__
    inline void
    dec_out_edges( size_type value =1)
    {
      size_type start, finish;
      thrust::tie( start, finish) =m_out_edges.get();
      m_out_edges.set( start, finish - value);
    }

    __host__ __device__
    inline void
    inc_out_edge_shift()
    {
      if( !m_out_edges.empty()){
	m_out_edges.inc();
      }
    }

    __host__ __device__
    inline void
    dec_out_edge_shift( size_type value =1)
    {
      m_out_edges.dec( value);
    }

    __host__
    friend std::ostream& operator<<( std::ostream& os, const stored_vertex& sv) {
      os <<"[" <<*(sv.m_out_edges.begin())
         <<"," <<*(sv.m_out_edges.end())
         <<"]";
      /*stored_vertex::edge_iterator iter, last;
        thrust::tie( iter, last) =sv.out_edges();
        for( ; iter !=last; ++iter){
        os <<*iter <<",";
        }*/
      return os;
    }
  };

  template <typename SizeType, typename VertexProperty>
  struct bidir_stored_vertex
    : public stored_vertex<SizeType,VertexProperty>
  {
    typedef SizeType size_type;
    
  public:
    thrust::integer_range<size_type,thrust::device_space_tag> m_in_edges;    
    VertexProperty m_property;
    
  public:
    bidir_stored_vertex() {}

  public:
    __host__ __device__
    inline size_type
    getInDegree() const
    {
      return m_in_edges.size();
    }
    
    __host__ __device__
    inline thrust::pair<size_type,size_type>
    in_edges() const
    {
      return thrust::make_pair( *(m_in_edges.begin()), *(m_in_edges.end()));
    }

    __host__ __device__
    inline void
    set_in_edges( size_type start, size_type finish)
    {
      m_in_edges.set( start, finish);
    }
    
    __host__ __device__
    inline void
    inc_in_edges()
    {
      size_type start, finish;
      thrust::tie( start, finish) =m_in_edges.get();
      m_in_edges.set( start, ++finish);
    }

    __host__ __device__
    inline void
    dec_in_edges( size_type value =1)
    {
      size_type start, finish;
      thrust::tie( start, finish) =m_in_edges.get();
      m_in_edges.set( start, finish - value);
    }

    __host__ __device__
    inline void
    inc_in_edge_shift()
    {
      if( !m_in_edges.empty()){
        m_in_edges.inc();
      }
    }
    
    __host__ __device__
    inline void
    dec_in_edge_shift( size_type value =1)
    {
      m_in_edges.dec( value);
    }

    __host__
    friend std::ostream& operator<<( std::ostream& os, const bidir_stored_vertex& sv) {
      os <<", out_degree:" <<sv.getOutDegree()
         <<", in_degree:" <<sv.getInDegree();
      return os;
    }
  };
  
  template <typename StoredVertex>
  struct inc_out_edge_shift
  {
    __host__ __device__
    inline void operator()( StoredVertex& sv)
    {
      sv.inc_out_edge_shift();
    }
  };
  
  template <typename StoredVertex>
  struct dec_out_edge_shift
  {
    unsigned int m_value;
    
    dec_out_edge_shift( unsigned int value)
      : m_value(value) {}
    
    __host__ __device__
    inline void operator()( StoredVertex& sv)
    {
      sv.dec_out_edge_shift(m_value);
    }
  };

  template <typename StoredVertex>
  struct inc_in_edge_shift
  {
    __host__ __device__
    inline void operator()( StoredVertex& sv)
    {
      sv.inc_in_edge_shift();
    }
  };
  
  template <typename StoredVertex>
  struct dec_in_edge_shift
  {
    unsigned int m_value;
    
    dec_in_edge_shift( unsigned int value)
      : m_value(value) {}
    
    __host__ __device__
    inline void operator()( StoredVertex& sv)
    {
      sv.dec_in_edge_shift(m_value);
    }
  };
} //end namespace thrust
